﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ParserCore
{
    public partial class Parser
    {
        private enum Separator { NOSEPARATOR, COMMA, SEMICOLON, NEWLINE }

        public bool ParserDebug { get; set; }

        public ParserCore.Statement StatementTree { get; private set; }

        public Parser(Scanner scanner)
            : base(scanner)
        {
            /* */
        }

        public void Reset()
        {
            ParserDebug = false;
            StatementTree = null;
        }

        // pomocne stvari

        private void PrintDebugInfo(string msg)
        {
            if (ParserDebug)
            {
                Console.WriteLine(msg);
            }
        }
    }

    public class GNonTerminalTree { }

    // slede strukture koje se koriste da se konstruise if-then-else statement
    public class GIfCmdList : GNonTerminalTree { }
    public class GIfCmdList1 : GNonTerminalTree { }

    // else_clause : ELSE stash_comment opt_sep opt_list
    public class GElseClause : GNonTerminalTree
    {
        public Statement Action
        { get; private set; }

        public GElseClause(Statement action)
            : base()
        { Action = action; }
    }

    // elseif_clause : ELSEIF stash_comment opt_sep expression opt_sep opt_list
    public class GElseIfClause : GNonTerminalTree
    {
        public Expression Condition
        { get; private set; }

        public Statement Action
        { get; private set; }

        public GElseIfClause(Expression condition, Statement action)
            : base()
        {
            Condition = condition;
            Action = action;
        }
    }

    // if_cmd_list : if_cmd_list1
    public class GIfCmdList_IfCmdList1 : GIfCmdList
    {
        public GIfCmdList1 IfCmdList1
        { get; private set; }

        public GIfCmdList_IfCmdList1(GIfCmdList1 ifCmdList1)
            : base()
        { IfCmdList1 = ifCmdList1; }
    }

    // if_cmd_list : if_cmd_list1 else_clause
    public class GIfCmdList_IfCmdList1_ElseClause : GIfCmdList
    {
        public GIfCmdList1 IfCmdList1
        { get; private set; }

        public GElseClause ElseClause
        { get; private set; }

        public GIfCmdList_IfCmdList1_ElseClause(GIfCmdList1 ifCmdList1, GElseClause elseClause)
            : base()
        {
            IfCmdList1 = ifCmdList1;
            ElseClause = elseClause;
        }
    }

    // if_cmd_list1 : expression opt_sep opt_list
    public class GIfCmdList1_Expression_OptSep_OptList : GIfCmdList1
    {
        public Expression Condition
        { get; private set; }

        public Statement Action
        { get; private set; }

        public GIfCmdList1_Expression_OptSep_OptList(Expression condition, Statement action)
            : base()
        {
            Condition = condition;
            Action = action;
        }
    }

    // if_cmd_list1 : if_cmd_list1 elseif_clause
    public class GIfCmdList1_IfCmdList1_ElseifClause : GIfCmdList1
    {
        public GIfCmdList1 IfCmdList1
        { get; private set; }

        public GElseIfClause ElseIfClause
        { get; private set; }

        public GIfCmdList1_IfCmdList1_ElseifClause(GIfCmdList1 ifCmdList1, GElseIfClause elseIfClause)
            : base()
        {
            IfCmdList1 = ifCmdList1;
            ElseIfClause = elseIfClause;
        }
    }

    // if-then-else tree visitor
    public class IfThenElseVisitor
    {
        private List<object> ifThenElseList;

        public Statement IfThenElseStatement
        {
            get
            {
                var ifThenElseArr = ifThenElseList.ToArray();
	            var ifThenElseArrLen = ifThenElseArr.Length;

	            Statement tmpStat = new IfThenElseStatement(
								(Expression) ifThenElseArr[ifThenElseArrLen - 3],
								(Statement) ifThenElseArr[ifThenElseArrLen - 2],
								(Statement) ifThenElseArr[ifThenElseArrLen - 1]
								);
	            ifThenElseArrLen -= 3;
	            for(;ifThenElseArrLen>1;)
	            {
	                tmpStat = new IfThenElseStatement(
						(Expression) ifThenElseArr[ifThenElseArrLen - 2],
						(Statement) ifThenElseArr[ifThenElseArrLen - 1],
						tmpStat
						);
	                ifThenElseArrLen -= 2;
	            }
	            return tmpStat;
            }
        }

        public IfThenElseVisitor(GIfCmdList ifCmdList)
        {
            ifThenElseList = new List<object>();
            Visit(ifCmdList);   
        }

        private void Visit(GIfCmdList ifCmdList)
        {
            if (ifCmdList is GIfCmdList_IfCmdList1)
                Visit1((GIfCmdList_IfCmdList1)ifCmdList);
            else
                Visit2((GIfCmdList_IfCmdList1_ElseClause)ifCmdList);
        }

        private void Visit1(GIfCmdList_IfCmdList1 ifCmdList)
        {
            Visit3(ifCmdList.IfCmdList1);
            ifThenElseList.Add(new BlockStatement(new Statement[] { }));
        }

        private void Visit2(GIfCmdList_IfCmdList1_ElseClause ifCmdList)
        {
            Visit3(ifCmdList.IfCmdList1);
            Visit7(ifCmdList.ElseClause);
        }

        private void Visit3(GIfCmdList1 ifCmdList1)
        {
            if (ifCmdList1 is GIfCmdList1_Expression_OptSep_OptList)
                Visit4((GIfCmdList1_Expression_OptSep_OptList)ifCmdList1);
            else
                Visit5((GIfCmdList1_IfCmdList1_ElseifClause) ifCmdList1);
        }

        private void Visit4(GIfCmdList1_Expression_OptSep_OptList ifCmdList1)
        {
            ifThenElseList.Add(ifCmdList1.Condition);
            ifThenElseList.Add(ifCmdList1.Action);
        }

        private void Visit5(GIfCmdList1_IfCmdList1_ElseifClause ifCmdList1)
        {
            Visit3(ifCmdList1.IfCmdList1);
            Visit6(ifCmdList1.ElseIfClause);
        }

        private void Visit7(GElseClause elseClause)
        {
            ifThenElseList.Add(elseClause.Action);
        }

        private void Visit6(GElseIfClause elseIfClause)
        {
            ifThenElseList.Add(elseIfClause.Condition);
            ifThenElseList.Add(elseIfClause.Action);
        }
    }

    // switch-case

    // switch_command : SWITCH stash_comment expression opt_sep case_list END
    public class GSwitchCommand : GNonTerminalTree
    {
        public Expression Expr
        { get; private set; }

        public GCaseList CaseList
        { get; private set; }

        public GSwitchCommand(Expression expr, GCaseList caseList)
            : base()
        {
            Expr = expr;
            CaseList = caseList;
        }
    }

    // case_list
    public class GCaseList : GNonTerminalTree { }
    public class GCaseList1 : GNonTerminalTree { }

    // case_list : /* empty */
    public class GCaseList_Empty : GCaseList { }
    
    // case_list : case_list1
    public class GCaseList_CaseList1 : GCaseList
    {
        public GCaseList1 CaseList1
        { get; private set; }

        public GCaseList_CaseList1(GCaseList1 caseList1)
            : base()
        { CaseList1 = caseList1; }
    }

    // case_list : case_list1 default_case
    public class GCaseList_CaseList1_DefaultCase : GCaseList
    {
        public GCaseList1 CaseList1
        { get; private set; }

        public GDefaultCase DefaultCase
        { get; private set; }

        public GCaseList_CaseList1_DefaultCase(GCaseList1 caseList1, GDefaultCase defaultCase)
            : base()
        {
            CaseList1 = caseList1;
            DefaultCase = defaultCase;
        }
    }

    // case_list1 : switch_case
    public class GCaseList1_SwitchCase : GCaseList1
    {
        public GSwitchCase SwitchCase
        { get; private set; }

        public GCaseList1_SwitchCase(GSwitchCase switchCase)
            : base()
        { SwitchCase = switchCase; }
    }

    // case_list1 : case_list1 switch_case
    public class GCaseList1_CaseList1_SwitchCase : GCaseList1
    {
        public GCaseList1 CaseList1
        { get; private set; }

        public GSwitchCase SwitchCase
        { get; private set; }

        public GCaseList1_CaseList1_SwitchCase(GCaseList1 caseList1, GSwitchCase switchCase)
            : base()
        {
            CaseList1 = caseList1;
            SwitchCase = switchCase;
        }
    }

    // switch_case	: CASE stash_comment opt_sep expression opt_sep opt_list
    public class GSwitchCase : GNonTerminalTree
    {
        public Expression Case
        { get; private set; }

        public Statement Action
        { get; private set; }

        public GSwitchCase(Expression case1, Statement action)
            : base()
        {
            Case = case1;
            Action = action;
        }
    }
    // default_case : OTHERWISE stash_comment opt_sep opt_list
    public class GDefaultCase : GNonTerminalTree
    {
        public Statement Action
        { get; private set; }

        public GDefaultCase(Statement action)
            : base()
        { Action = action; }
    }
   
    public class SwitchCaseVisitor
    {
        private Expression expr;
        private List<Tuple<Expression, Statement>> caseTupleList;
        private Statement defaultStatement;

        public Statement SwitchCaseStatement
        {
            get
            {
                var caseTupleArray = new Tuple<Expression, Statement>[caseTupleList.Count];

                for (int i = 0; i < caseTupleList.Count; i++)
                    caseTupleArray[i] = caseTupleList[i];

                return new SwitchCaseStatement(expr, defaultStatement, caseTupleArray);
            }
        }

        public SwitchCaseVisitor(GSwitchCommand switchCommand)
        {
            caseTupleList = new List<Tuple<Expression, Statement>>();
            Visit(switchCommand);
        }

        private void Visit(GSwitchCommand switchCommand)
        {
            expr = switchCommand.Expr;
            Visit1(switchCommand.CaseList);
        }

        private void Visit1(GCaseList caseList)
        {
            if (caseList is GCaseList_CaseList1)
                Visit2((GCaseList_CaseList1)caseList);
            else if (caseList is GCaseList_CaseList1_DefaultCase)
                Visit3((GCaseList_CaseList1_DefaultCase)caseList);
            else
            { }
        }

        private void Visit2(GCaseList_CaseList1 c)
        {
            Visit4(c.CaseList1);
        }

        private void Visit3(GCaseList_CaseList1_DefaultCase c)
        {
            Visit4(c.CaseList1);
            Visit5(c.DefaultCase);
        }

        private void Visit4(GCaseList1 caseList1)
        {
            if (caseList1 is GCaseList1_SwitchCase)
                Visit6((GCaseList1_SwitchCase)caseList1);
            else
                Visit7((GCaseList1_CaseList1_SwitchCase)caseList1);
        }

        private void Visit5(GDefaultCase defaultCase)
        {
            defaultStatement = defaultCase.Action;
        }

        private void Visit6(GCaseList1_SwitchCase c)
        {
            Visit8(c.SwitchCase);
        }

        private void Visit7(GCaseList1_CaseList1_SwitchCase c)
        {
            Visit4(c.CaseList1);
            Visit8(c.SwitchCase);
        }

        private void Visit8(GSwitchCase switchCase)
        {
            caseTupleList.Add(new Tuple<Expression, Statement>(switchCase.Case, switchCase.Action));
        }
    }

    // function-definition

    public class GFunction : GNonTerminalTree { }
    public class GFunction1 : GNonTerminalTree { }
    public class GFunction2 : GNonTerminalTree { }

    // function : function_beg function1 
    public class GFunction_FCN_Function1 : GFunction
    {
        public GFunction1 Function1
        { get; private set; }

        public GFunction_FCN_Function1(GFunction1 function1)
            : base()
        { Function1 = function1; }
    }

    // function : function_beg return_list '=' function1 
    public class GFunction_FCN_ReturnList_Function1 : GFunction
    {
        public Identifier[] ReturnList
        { get; private set; }

        public GFunction1 Function1
        { get; private set; }

        public GFunction_FCN_ReturnList_Function1(Identifier[] returnList, GFunction1 function1)
            : base()
        {
            ReturnList = returnList;
            Function1 = function1;
        }
    }

    // function1 : fcn_name function2
    public class GFunction1_FcnName_Function2 : GFunction1
    {
        public Identifier Name
        { get; private set; }

        public GFunction2 Function2
        { get; private set; }

        public GFunction1_FcnName_Function2(Identifier name, GFunction2 function2)
            : base()
        {
            Name = name;
            Function2 = function2;
        }
    }

    // function2 : param_list opt_sep opt_list function_end
    public class GFunction2_ParamList_OptList : GFunction2
    {
        public Identifier[] Parameters
        { get; private set; }

        public Statement OptList
        { get; private set; }

        public GFunction2_ParamList_OptList(Identifier[] parameters, Statement optList)
            : base()
        {
            Parameters = parameters;
            OptList = optList;
        }
    }

    // function2 : opt_sep opt_list function_end
    public class GFunction2_OptList : GFunction2
    {
        public Statement OptList
        { get; private set; }

        public GFunction2_OptList(Statement optList)
            : base()
        {
            OptList = optList;
        }
    
    }

    public class FunctionDefinitionVisitor
    {
        private Identifier[] ReturnList;
        private Identifier[] ParameterList;
        private Identifier FcnName;
        private Statement FunctionBody;

        public Statement FunctionDefinition
        {
            get
            {
                return new FunctionDefinitionStatement(FcnName, ReturnList, ParameterList, FunctionBody);
            }
        }

        public FunctionDefinitionVisitor(GFunction function)
        {
            Visit(function);
        }

        private void Visit(GFunction function)
        {
            if (function is GFunction_FCN_Function1)
                Visit1((GFunction_FCN_Function1)function);
            else
                Visit2((GFunction_FCN_ReturnList_Function1)function);
        }

        private void Visit1(GFunction_FCN_Function1 f)
        {
            ReturnList = new Identifier[] { };
            Visit3(f.Function1);
        }

        private void Visit2(GFunction_FCN_ReturnList_Function1 f)
        {
            ReturnList = f.ReturnList;
            Visit3(f.Function1);
        }

        private void Visit3(GFunction1 f)
        {
            var g = (GFunction1_FcnName_Function2)f;
            FcnName = g.Name;
            Visit4(g.Function2);
        }

        private void Visit4(GFunction2 f)
        {
            if (f is GFunction2_OptList)
                Visit5((GFunction2_OptList)f);
            else
                Visit6((GFunction2_ParamList_OptList)f);
        }

        private void Visit5(GFunction2_OptList f)
        {
            ParameterList = new Identifier[] { };
            FunctionBody = f.OptList;
        }

        private void Visit6(GFunction2_ParamList_OptList f)
        {
            ParameterList = f.Parameters;
            FunctionBody = f.OptList;
        }
    }
}
